﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;

namespace SharpSoft.Maths
{
    /// <summary>
    /// 向量辅助工具
    /// </summary>
    /// <remarks>
    /// 向量点积为1表示两向量同向，为-1表示两向量相反
    /// 
    /// </remarks>
    public static class VectorHelper
    {
        /// <summary>
        /// Negates the values of X and Y on this Vector
        /// </summary>
        public static Vector2 Negate(this Vector2 v)
        {
            return new Vector2(-v.X, -v.Y);
        }

        /// <summary>
        /// Normalize - return a new Vector of this Vector to maintain its direction, but to have a length
        /// of 1.  This is equivalent to dividing this Vector by Length
        /// </summary>
        public static Vector2 Normalize(this Vector2 v)
        {
            var max = MathF.Max(MathF.Abs(v.X), MathF.Abs(v.Y));
            var length = v.Length();
            Vector2 v2 = new Vector2((v.X / max) / length, (v.Y / max) / length);
            return v2;
        }
        public static Vector3 Normalize(this Vector3 v)
        {
            Vector3 v2 = new Vector3(v.X, v.Y, v.Z);
            // Computation of length can overflow easily because it
            // first computes squared length, so we first divide by
            // the largest coefficient.
            float m = MathF.Abs(v2.X);
            float absy = MathF.Abs(v2.Y);
            float absz = MathF.Abs(v2.Z);
            if (absy > m)
            {
                m = absy;
            }
            if (absz > m)
            {
                m = absz;
            }

            v2.X /= m;
            v2.Y /= m;
            v2.Z /= m;

            float length = v2.Length();
            return v2 / length;
        }

        /// <summary>
        /// CrossProduct - Returns the cross product: vector1.X*vector2.Y - vector1.Y*vector2.X
        /// </summary>
        /// <returns>
        /// Returns the cross product: vector1.X*vector2.Y - vector1.Y*vector2.X
        /// </returns>
        /// <param name="vector1"> The first Vector </param>
        /// <param name="vector2"> The second Vector </param>
        public static float CrossProduct(Vector2 vector1, Vector2 vector2)
        {

            return vector1.X * vector2.Y - vector1.Y * vector2.X;
        }



        /// <summary>
        /// 获取v2在v1上的投影向量
        /// </summary>
        /// <param name="v1">基向量</param>
        /// <param name="v2"></param>
        /// <returns></returns>
        public static Vector2 Projection(Vector2 v1, Vector2 v2)
        {
            var v1l = v1.Length();
            var l = Vector2.Dot(v1, v2) / v1l;
            v1 = AddLength(v1, l - v1l);
            return v1;
        }
        //public static Vector2 Projection1(Vector2 v1, Vector2 v2)
        //{
        //    // var v = v1 - Vector2.Dot(v2, v1) * v1;
        //    var v = (Vector2.Dot(v2, v1) - 1) * v1;
        //    return v;
        //}

        /// <summary>
        /// 获取两个向量的转向角(以弧度记)（在笛卡尔坐标系中X轴正方向往Y轴正方向旋转为正向旋转（逆时针），往Y轴负方向旋转为逆向旋转（顺时针）。）
        /// </summary>
        /// <param name="vector1"></param>
        /// <param name="vector2"></param>
        /// <returns></returns>
        public static float AngleBetween(Vector2 vector1, Vector2 vector2)
        {
            var sin = vector1.X * vector2.Y - vector2.X * vector1.Y;
            var cos = vector1.X * vector2.X + vector1.Y * vector2.Y;
            return MathF.Atan2(sin, cos);
        }
        /// <summary>
        /// 将当前向量绕向量起点旋转指定的角度得到一个新的向量
        /// </summary>
        /// <param name="radian"></param>
        /// <returns></returns>
        public static Vector2 Rotate(this Vector2 src, float radian)
        {
            /*
 x1 = x0 * cos（-B） - y0 * sin（-B） =>  x1 = x0 * cosB + y0 * sinB
 y1 = x0 * sin（-B） + y0 * cos（-B）=>  y1 = -x0 * sinB + y0 * cosB
             */

            var x1 = src.X * MathF.Cos(radian) + src.Y * MathF.Sin(radian);
            var y1 = -src.X * MathF.Sin(radian) + src.Y * MathF.Cos(radian);
            return new Vector2(x1, y1);
        }
        /// <summary>
        /// 增加向量的长度
        /// </summary>
        public static Vector2 AddLength(Vector2 src, float length_incr)
        {
            var l = src.Length();
            if (l == 0)
            {
                throw new Exception("无方向的向量不能更改其长度。");
            }
            var nl = l + length_incr;
            var b = nl / l;
            float _x = b * src.X;
            float _y = b * src.Y;
            return new Vector2(_x, _y);
        }

        /// <summary>
        /// 两向量是否平行
        /// </summary>
        /// <param name="lhs"></param>
        /// <param name="rhs"></param>
        /// <returns></returns>
        public static bool IsParallel(Vector2 lhs, Vector2 rhs)
        {
            float value = Vector2.Dot(lhs, rhs);
            if (MathF.Abs(value) == 1)
                return true;
            return false;
        }
    }
}
